#include "./fnv1a_hash.cpp"

constexpr bool REDIRECT_DEFAULT = true; // Change to "false" for debugging.

namespace utils {
    // bool execCmdSimp(const std::string& cmd, const std::string& dir = "") {
    //     fprintf(stderr, "Executing: %s\n", cmd.c_str());
    //     char* cmdCh = new char[cmd.size() + 1]; std::strcpy(cmdCh, cmd.c_str());
    //     STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi;
    //     if (!CreateProcess(NULL, cmdCh, NULL, NULL, FALSE,
    //         0, NULL, dir.c_str(), &si, &pi)) {delete cmdCh; return 0;}
    //     WaitForSingleObject(pi.hProcess, INFINITE);
    //     CloseHandle(pi.hProcess), CloseHandle(pi.hThread);
    //     delete cmdCh; return 1;
    // }
    std::string escapeQuotes(const std::string &str) {
        std::string res;
        res.reserve(str.size() + std::count(str.begin(), str.end(), '"'));
        for (const char &c : str) (c == '"') ? res += "\\\"" : res += c;
        return res;
    }
    bool execCmdSimp(const std::string& cmd, const std::string& dir = "",
            const bool& redirect = REDIRECT_DEFAULT) {
        fprintf(stderr, "Executing: %s\n", cmd.c_str());
        char currentDir[MAX_PATH]; GetCurrentDirectory(MAX_PATH, currentDir);
        if (!dir.empty() && !SetCurrentDirectory(dir.c_str())) return 0;
        std::string psCmd = "powershell -Command \"" + escapeQuotes(cmd) + "\"";
        if (redirect) psCmd += " >NUL 2>&1";
        int res = system(psCmd.c_str()); SetCurrentDirectory(currentDir);
        return res == 0;
    }
    void execCmd(const std::string& cmd, const std::string& dir = "",
            const bool& redirect = REDIRECT_DEFAULT) {
        stubborn(execCmdSimp, cmd, dir, redirect);
    }
    void compCpp(std::string &dst, AlgoType &algoType) {
        fprintf(stderr, "Compiling to %s.\n", dst.c_str());
        std::string par = algoType.genCompPara();
        execCmd("g++ ./src/main/main.cpp -o " + dst + par);
    }
    std::string getExec(AlgoType &algoType) {
        std::string dst = tempPath + \
            std::to_string(fnv1a_hash(algoType.serialize())) + ".exe";
        if (!fileExists(dst)) compCpp(dst, algoType);
        return dst;
    }
    bool chkAns(const std::string& path) {
        FILE* f = fopen(path.c_str(), "r"); int res = 0;
        fscanf(f, "R%d\n", &res); return res;
    }
    TestRes execCode(AlgoType algoType) {
        TestRes res;
        fprintf(stderr, "Module data: %s\n", algoType.serialize().c_str());
        std::string execPath = getExec(algoType);
        fprintf(stderr, "Got: %s\n", execPath.c_str());
        for (std::string &inFile : dataFiles) {
            std::string outFile = tempPath + std::to_string(fnv1a_hash(
                execPath + inFile)) + ".out";
            execCmd(execPath + " " + inFile + " " + outFile);
            bool ans = chkAns(outFile);
            res[inFile].first += ans, res[inFile].second += 1;
            fprintf(stderr, "Result: %d\n", ans);
        }
        return res;
    }
}

using utils::execCode;
